#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>


#define MAX_PATH_LENTH 256
#define LINE_LEN        80
#define TRUE 1
#define FALSE 0


struct command_save{
	char *name;
	int argc;
	char *argv[64];
};			//명령어 저장용

char *lookupPath(char **, char **);
int parsePath(char **);



int parse_command(char *strCommand, struct command_save * stCommand);
void print_prompt();
void readCommand(char *);
int isNeedIOChange(char **);	

////////////////////////////////////////////////////////////////
int main()
{
char *argv[256];
char commandLine[50];
struct command_save command;
int i, tmp;
int pid1, pid2, fid1, fid2;
char pathv[256][256];
char *name;	

parsePath((char**)pathv);

while(1){
int flag=1;
   print_prompt();
   readCommand(commandLine);

   if(strcmp(commandLine, "") == 0 ) continue;
   if(strcmp(commandLine, "exit") == 0) break;
	
   parse_command(commandLine, &command);



///////////////////////////////////////////////////




/////////////////////////////////////////////////////////////
   if (strcmp(command.argv[0], "cd") == 0) {
	if (chdir(command.argv[1]) != 0)
	     printf ("'%s' 디렉토리를 찾을 수 없습니다.  \n", command.argv[1]);
	else 
	     printf ("directory changed.\n");
	     continue;
	}
///////////////////////////////////////////////////cd구현
if (strcmp(command.argv[0], "print") == 0) {
	
	if(flag == 0) continue;
int i;
int state;
char buffer[200];


for(i=1 ; i<2 ;i++)
{
	FILE *file = fopen(command.argv[1],"r"); 
	if(file == NULL){ 
		printf("%s : No such file or directory\n",command.argv[1]);
		continue;
	}

	while(1)
	{
	fgets(buffer,sizeof(buffer),file); //버퍼 사이즈에 받은 파일
	if(feof(file) != 0) //file을 이용하여 FILE의 구조체 변수를참조한다
	break;	
	fputs(buffer,stdout);
	}
	state = fclose(file); //파일 종결
	if(state!=0){ // 숫자가 0이 아니면 에러
	printf("file close error!\n");
	return 1;
	}
	
}
continue;

}
///////////////////////////////////////////소스 결합 cat 기능 구현

if (strcmp(command.argv[0], "list") == 0) {

	if(flag == 0) continue;
 DIR *dirp;
 struct dirent *dentry;
 int lnt=0;

 if((dirp=opendir("."))==NULL) 
  exit(1);

 while(dentry=readdir(dirp)){ 

  if(dentry->d_ino!=0 && dentry->d_name[0] !='.'){ 
   printf("%-15s",dentry->d_name); 
   lnt++;
   if(lnt>=5){ 
     printf("\n");
     lnt=0;
   }
  }
 }
 printf("\n");
 closedir(dirp); 
continue;
}
///////////////////////////////////////ls기능 구현 


if (strcmp(command.argv[0], "copy") == 0) {
	if(flag == 0) continue;
    char *cmd1;
    char *cmd2;
 
    cmd1 = command.argv[1]; //file name
    cmd2 = command.argv[2]; //copy file name
 
    char block[BUFSIZ];
    int in, out;
    int nread;
 
    in = open(cmd1,O_RDONLY); //원본파일
    out = open(cmd2, O_WRONLY| O_CREAT, S_IRUSR| S_IWUSR);//만들파일
    nread = read(in,block,sizeof(block)); //읽은만큼 nread가 올라가고
    write(out,block,nread);          //read만큼 쓴다.
    continue;
 }
////////////////////////////////////////copy 구현




if (strcmp(command.argv[0], "cwd") == 0) {
	if(flag == 0) continue;
    char buffer[256];
    if(getcwd(buffer,256)==NULL) //getcwd 현재 작업 디렉토리 얻기 함수
    exit(1);
    printf("%s\n",buffer);

continue;
 }

////////////////////////////////////현재디렉토리 구현
if(strcmp(command.argv[0], "pro") == 0) {
	if(flag == 0) continue;
 DIR *dir;            //  /proc/pid/ 를 가리킬 DIR* 변수
 struct dirent *entry;  // 각 파일의 inode를 통해 파일을 선택할 dirent 구조체
 struct stat fileStat;  // 파일의 정보를 담는 구조체
 
 int pid;      // 프로세스는 /proc 디렉토리에 자신의 pid로 파일을 담아 둡니다.
 char cmdLine[256];
 char tempPath[256];
 
 printf("Processes Info\n");
 dir = opendir("/proc");   
 while ((entry = readdir(dir)) != NULL) { 
       lstat(entry->d_name, &fileStat);
       if (!S_ISDIR(fileStat.st_mode))  
              continue;        // 프로세스는 /proc에 자신의 pid로 디렉토리를
                               // 만드는 점을 안다면 이해하실거라 생각합니다.
       pid = atoi(entry->d_name);// 프로세스(디렉토리)인것을 확인하면, 숫자로 반환한다.
         if (pid <= 0) continue;     // 숫자가 아니라면 다시 continue;
       sprintf(tempPath, "/proc/%d/cmdline", pid); //cmdline::프로세스 이름이 적힌파일
       getCmdLine(tempPath, cmdLine);// /proc/pid/cmdline에서 프로세스의 이름을
                                   // 가져오는 함수로 보냅니다.
       printf("[%d] %s\n", pid, cmdLine);
     }
     closedir(dir);
continue;
}
 ////////////////////////////////////////////////////ps기능

if(strcmp(command.argv[0], "maked") == 0){
	if(flag == 0) continue;
int opt;
  int ch;
  char conin = 'h'; //문자형 이름을 h로 선언
  opt = getopt(command.argc, command.argv, "m:"); //프로그램을 실행할때 옵션주기
  if(opt == 'm') //옵션이 m이면 
  mkdir(command.argv[2], ch); //새로운 디렉토리의 허가모드로 지정한 모드로 설정
  else
  mkdir(command.argv[1], 0755); //부모 디렉토리가 존재하지 않은 경우 자동 생성한다. 
continue;
}
////////////////////////////////////////////////mkdir
if(strcmp(command.argv[0], "rmd") == 0) {
int rmdir(const char *path); //삭제될 디렉토리의이름은path 매개변수로 지정한다.
if(rmdir(command.argv[1])){
switch (errno){ //예외처리상황에대해서다음과같은상수를가진다. (errno.h)
  case ENOTEMPTY : printf("디렉토리가 비어있지 않습니다.\n");break;     
  case ENOENT : printf("해당 디렉토리가 존재하지 않습니다.\n");break;   
  case EACCES : printf("해당 디렉토리에 대한 권한이 없습니다.\n");break;
  default : printf("알수 없는 오류입니다.\n");
        }
  }
continue;
}
////////////////////////////////////////////////rmdir 
if(strcmp(command.argv[0], "move") == 0) {
const char* dest;  //문자열 조작 함수
 struct stat st;  //권한체크를 위한 할당   
 int i;      
 if (command.argc < 3) {  //인자갯수 저장변수가 3보다 크면      
  fprintf(stderr,"USAGE: %s <source...> <destination>\n", command.argv[0]);   
  return -1;     
 }      
 dest = command.argv[command.argc - 1]; 
 if (stat(dest, &st)) {             
  if (errno != ENOENT) {   //디렉토리나 파일이 존재하지 않음       
   fprintf(stderr, "failed on %s - %s\n", dest, strerror(errno));             
   return -1;         
  }         
  st.st_mode = 0;  //st의 파일타입   
 }      
 for (i = 1; i < command.argc - 1; i++) {         
  const char *source = command.argv[i];         
  char fullDest[PATH_MAX + 1 + PATH_MAX + 1];          
  if (strlen(dest) + 1 + strlen(source) + 1 > sizeof(fullDest)) {             
   fprintf(stderr, "path too long\n");             
   return -1;         
  }         
 
 
  strcpy(fullDest, dest);         
  if (S_ISDIR(st.st_mode)) {    //정규 파일이면         
   const char *fileName = strrchr(source, '/');             
   if (fullDest[strlen(fullDest)-1] != '/') {                 
    strcat(fullDest, "/"); //문자열 2개를 받아 첫번째 문자열 뒤에 두번째 문자열을 붙여서 전달해주는 함수            
   }             
  strcat(fullDest, fileName ? fileName + 1 : source);         
  }                   
  if (rename(source, fullDest)) {       //rename은 파일이름이나 디렉토리이름을 바꾸는 함수      
   fprintf(stderr, "failed on '%s' - %s\n", source, strerror(errno));             
   return -1;  //return -1은 에러가나면 종료 된다는 의미       
  }     
 }      
continue;
}

/////////////////////////////////////////////////////mv

command.name = lookupPath((char**)command.argv, (char**)pathv);
        if(command.name == NULL) {      // 실행가능한파일이없다면
        printf("not find\n");
                continue;
                }


if((tmp = isNeedIOChange(command.argv)) != 0)
{
        printf("needchangdfffffffffffffffffffffffffde : %d\n", tmp);
switch(command.argv[tmp][0])
{
        case '>':       // 출력을파일로변환한다
        if ( (pid1 = fork()) == 0 ) {
                // 차일드프로세서라면
        fid1 = open(command.argv[tmp+1], O_WRONLY | O_CREAT, 0777);
        close(1); // 출력닫기
        dup(fid1); // 출력을파일로변경
        for (i=0;i<tmp;i++)     // 아규먼트재정렬
                argv[i] = command.argv[i];
        argv[tmp] = NULL;
execvp(command.name, argv);     // 실행
        close(fid1);
        exit(1);
        }
        break;
}
}






















	}
}


/////////////////////////////////////////////////////////////////////////////
void print_prompt()
{
    char promptString[MAX_PATH_LENTH] = "(SHELL) :";
    char buffer[MAX_PATH_LENTH];
    getcwd(buffer, MAX_PATH_LENTH);

    strcat(promptString, buffer);

    printf("-%s $ ", promptString);
}



void readCommand(char *buffer){
	gets(buffer);
}


int parse_command(char *strCommand, struct command_save *stCommand)
{
   int count = 0;
   int posi = 0;
   int s_posi = 0;
   int i;
   char *argv[64];
   char is_command = FALSE;

   argv[count] = malloc(64);
   strcat(strCommand, " ");

   while(strCommand[posi] != '\0'){
	if(strCommand[posi] == ' ')
 	  if(is_command == FALSE){
	     posi++;
	     continue;
	  }
	else{
		argv[count][s_posi]= '\0';
		s_posi = 0;
		posi++;
		count++;
		argv[count] = malloc(64);
		is_command = FALSE;
		continue;
	    }
	argv[count][s_posi++] = strCommand[posi++];
	is_command = TRUE;
   }
   stCommand->argc = count;
   free(argv[count]);

   for(i=0; i < stCommand->argc; i++)
	stCommand->argv[i] = argv[i];

   stCommand->argv[stCommand->argc] = NULL;

   return 1;

}

int isNeedIOChange(char ** argv) {
	int i;
	for (i=0;i<256;i++) {	// 아규먼트를검색하여
		if (argv[i] == NULL) break;	
		if (strcmp(argv[i],"<") == 0) return i;// 해당배열의위치를리턴
		if (strcmp(argv[i],">") == 0) return i;
		if (strcmp(argv[i],"|") == 0) return i;
	}
	return 0;	// 존재하지않으면0리턴
}



int parsePath(char *dirs[]) {
	char *pathEnvVar;
	char *thePath;
	int i;
	int pos = 0;
	int spos = 0;
	int cnt = 0;

	// 배열의기본값초기화
	for (i=0; i<256; i++)
		dirs[i] = NULL;

	// PATH 환경변수읽어오기
	pathEnvVar = (char*) getenv("PATH");
	thePath = (char *)malloc(strlen(pathEnvVar) + 1);
	strcpy(thePath, pathEnvVar);

	// PATH에저장된디렉토리를배열에담는다
	dirs[cnt] = (char *)malloc(256);
	while (thePath[pos] != '\0') {
		if (thePath[pos] == ':') {
			dirs[cnt][spos++] = '/'; // 마지막에'/'를추가해준다
			dirs[cnt++][spos] = '\0';
			dirs[cnt] = (char *)malloc(256);
			spos = 0;
			pos++;
			continue;
		}
		dirs[cnt][spos++] = thePath[pos++];
	}
	dirs[cnt][spos++] = '/'; // 마지막에'/'를추가해주어서뒤에파일이름을
	dirs[cnt++][spos] = '\0'; // 붙일수있도록한다.

	return TRUE;
}

char *lookupPath(char **argv, char **dir) {
	char *result;
	char pName[256];
	struct dirent *dentry; // 현재디렉토리에대한정보를저장하는구조체
	int i;

	if(*argv[0] == '/' || *argv[0] == '.') { // 이미주소가있다면그대로사용
		if (access(argv[0],X_OK) == 0) { // 실행가능하다면절대경로를포함한
			result = malloc(64); // 파일이름리턴
			strcpy(result, argv[0]);
			return result;
		}
		else {	// 실행가능하지않다면널값리턴
			fprintf(stderr, "%s : command not found\n", argv[0]);
			return NULL;
		}
	}

	result = malloc(256);
	// PATH중에프로그램이있는지검사
	for (i=0; i<256; i++) {
		if (dir[i] == NULL) break;
		strcpy (result,dir[i]);		// 절대경로를붙임
		strcat (result,argv[0]);
		if (access(result,X_OK) == 0) 	// 실행가능하다면리턴
			return result;
	}

	// 실행가능한요소가없다면널값리턴
	fprintf(stderr, "%s : command not found\n", argv[0]);
	free(result);
	return NULL;
}



int getCmdLine(char *file, char *buf) {
     FILE *srcFp;
     int i;
     srcFp = fopen(file, "r");  //   /proc/pid/cmdline에 이름이 있습니다.
    memset(buf, 0, sizeof(buf));
     fgets(buf, 256, srcFp);
     fclose(srcFp);
 }
 

